package org.study.redisson.boot.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisServerCommands;
import org.springframework.data.redis.core.*;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.function.Function;

/**
 * Redis缓存监控。参考 https://www.imooc.com/article/325132
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2025年11月20日
 */
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/redis/monitor")
public class RedisController {
    private final RedisTemplate redisTemplate;
    private final StringRedisTemplate stringRedisTemplate;

    @GetMapping("/v1")
    public Map<String, Object> infoV1() {
        Long dbSize = (Long) redisTemplate.execute((RedisCallback<Long>) redisConnection -> redisConnection.serverCommands().dbSize());
        Long dbSize1 = stringRedisTemplate.execute((RedisCallback<Long>) redisConnection -> redisConnection.serverCommands().dbSize());
        Properties info = (Properties) redisTemplate.execute((RedisCallback<Properties>) redisConnection -> redisConnection.serverCommands().info());
        Properties info1 = stringRedisTemplate.execute((RedisCallback<Properties>) redisConnection -> redisConnection.serverCommands().info());
        Properties memory = (Properties) redisTemplate.execute((RedisCallback<Properties>) redisConnection -> redisConnection.serverCommands().info("memory"));
        Properties memory1 = stringRedisTemplate.execute((RedisCallback<Properties>) redisConnection -> redisConnection.serverCommands().info("memory"));
        Properties commandStats = (Properties) redisTemplate.execute((RedisCallback<Properties>) redisConnection -> redisConnection.serverCommands().info("commandstats"));
        Properties commandStats1 = stringRedisTemplate.execute((RedisCallback<Properties>) redisConnection -> redisConnection.serverCommands().info("commandstats"));
        List<Map<String, String>> pieList = Lists.newArrayList();
        commandStats.stringPropertyNames().forEach(key -> {
            String property = commandStats.getProperty(key);
            Map<String, String> data = new LinkedHashMap<>() {{
                put("name", StrUtil.removePrefix(key, "cmdstat_"));
                put("value", StrUtil.subBetween(property, "calls=", ",usec"));
            }};
            pieList.add(data);
        });
        return Map.of("time", DateUtil.now(), "dbSize", dbSize, "info", info, "memory", memory, "commandStats", pieList);
    }

    @GetMapping("/v2")
    public Map<String, Object> infoV2() {
        Long dbSize = redisServerCommands(RedisServerCommands::dbSize);
        Long dbSize1 = redisServerCommands1(RedisServerCommands::dbSize);
        Properties info = redisServerCommands(cmd -> cmd.info());
        Properties info1 = redisServerCommands1(cmd -> cmd.info());
        Properties memory = redisServerCommands(cmd -> cmd.info("memory"));
        Properties memory1 = redisServerCommands1(cmd -> cmd.info("memory"));
        Properties commandStats = redisServerCommands(cmd -> cmd.info("commandstats"));
        Properties commandStats1 = redisServerCommands1(cmd -> cmd.info("commandstats"));
        List<Map<String, String>> pieList = Lists.newArrayList();
        commandStats.stringPropertyNames().forEach(key -> {
            String property = commandStats.getProperty(key);
            Map<String, String> data = new LinkedHashMap<>() {{
                put("name", StrUtil.removePrefix(key, "cmdstat_"));
                put("value", StrUtil.subBetween(property, "calls=", ",usec"));
            }};
            pieList.add(data);
        });
        return Map.of("time", DateUtil.now(), "dbSize", dbSize, "info", info, "memory", memory, "commandStats", pieList);
    }

    @GetMapping("/v3")
    public Map<String, Object> infoV3() {
        Map<String, Object> redisMap = (Map<String, Object>) redisTemplate.execute((RedisConnection connection) -> {
            RedisServerCommands server = connection.serverCommands();
            Map<String, Object> dataMap = new LinkedHashMap<>() {{
                put("dbSize", server.dbSize());
                put("info", server.info());
                put("memory", server.info("memory"));
                put("commandStats", server.info("commandstats"));
            }};
            return dataMap;
        });
        Map<String, Object> redisMap1 = stringRedisTemplate.execute((RedisConnection connection) -> {
            RedisServerCommands server = connection.serverCommands();
            Map<String, Object> dataMap = new LinkedHashMap<>() {{
                put("dbSize", server.dbSize());
                put("info", server.info());
                put("memory", server.info("memory"));
                put("commandStats", server.info("commandstats"));
            }};
            return dataMap;
        });
        Map<String, Object> result = new LinkedHashMap<>() {{
            put("time", DateUtil.now());
            putAll(redisMap);
        }};
        return result;
    }

    /**
     * 查询redis信息，info()参数选项：server(有关Redis服务器的常规信息)、clients(客户端连接部分)、memory(内存消耗相关信息)、
     * persistence(RDB和AOF相关信息)、stats(一般统计)、replication(主/副本复制信息)、cpu(CPU消耗统计信息)、
     * commandstats(Redis命令统计)、cluster(Redis群集部分)、keyspace(与数据库相关的统计)、all(所有部分)、default
     */
    @GetMapping("/all")
    public Map<String, Object> infoAll() {
        Map<String, Object> redisMap = (Map<String, Object>) redisTemplate.execute((RedisConnection connection) -> {
            RedisServerCommands server = connection.serverCommands();
            Map<String, Object> dataMap = new LinkedHashMap<>() {{
                put("time", server.time());
                put("clientName", server.getClientName());
                put("clientList", server.getClientList());
                put("lastSave", server.lastSave());
                put("getConfig", server.getConfig("*"));
                put("dbSize", server.dbSize());
                put("info", server.info());
                put("info_server", server.info("server"));
                put("info_clients", server.info("clients"));
                put("info_memory", server.info("memory"));
                put("info_persistence", server.info("persistence"));
                put("info_stats", server.info("stats"));
                put("info_replication", server.info("replication"));
                put("info_cpu", server.info("cpu"));
                put("info_commandStats", server.info("commandstats"));
                put("info_cluster", server.info("cluster"));
                put("info_keyspace", server.info("keyspace"));
                put("info_all", server.info("all"));
                put("info_default", server.info("default"));
            }};
            return dataMap;
        });
        return redisMap;
    }

    private <T> T redisServerCommands(Function<RedisServerCommands, T> command) {
        return (T) redisTemplate.execute((RedisCallback<T>) redisConnection -> command.apply(redisConnection.serverCommands()));
    }

    private <T> T redisServerCommands1(Function<RedisServerCommands, T> command) {
        return (T) stringRedisTemplate.execute((RedisCallback<T>) redisConnection -> command.apply(redisConnection.serverCommands()));
    }
}
